package com.hirsi.core.util;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 压缩，单线程
 */
@Slf4j
public class RarUtil {

    private static String RAR_PATH = "/config/rar";

    public static Integer taskId;

    private static Integer progress;

    private static List<String> logs = new ArrayList<>();

    static {
        if (System.getProperty("os.name").contains("Windows")) RAR_PATH = "D:\\WinRAR\\Rar.exe";
        else {
            if (!FileUtil.exist(RAR_PATH)) FileUtil.writeFromStream(RarUtil.class.getResourceAsStream("/rar/rar"), RAR_PATH);
        }
    }

    /**
     * 压缩文件夹或单文件到源文件夹
     * @param sourcePath 源文件路径
     */
    private static void compress(String sourcePath) {
        compress(sourcePath, null);
    }

    /**
     * 压缩文件夹下的指定文件到源文件夹
     * @param sourcePath    源文件路径
     * @param filePaths     文件集合
     */
    private static void compress(String sourcePath, List<String> filePaths) {
        compress(sourcePath, filePaths, null);
    }

    /**
     * 压缩文件夹下的指定文件到指定目录/文件
     * @param sourcePath    源文件路径
     * @param filePaths     文件集合
     * @param targetPath    指定目录/文件
     */
    private static void compress(String sourcePath, List<String> filePaths, String targetPath) {
        compress(sourcePath, filePaths, targetPath, null);
    }

    /**
     * 带密码压缩文件夹下的指定文件到指定目录/文件
     * @param sourcePath    源文件路径
     * @param filePaths     文件集合
     * @param targetPath    指定目录/文件
     * @param password      密码
     */
    private static void compress(String sourcePath, List<String> filePaths, String targetPath, String password) {
        compress(sourcePath, filePaths, targetPath, password, null, null);
    }

    /**
     * 带密码、分卷、设置恢复记录压缩
     * @param sourcePath    原文件路径
     * @param targetPath    目标路径
     * @param password      密码
     * @param volume        分卷大小
     * @param rr            恢复记录大小
     */
    private static String compress(String sourcePath, List<String> filePaths, String targetPath, String password, String volume, Integer rr) {

        if (sourcePath == null || sourcePath.isEmpty()) throw new NullPointerException("源文件路径不能为空");
        if (rr != null && (rr < 1 || rr > 100)) throw new RuntimeException("恢复记录为1%~100%");
        File sourceFile = new File(sourcePath);
        if (!sourceFile.exists()) throw new RuntimeException("源文件路径不存在");

        String fileName = null, suffix;
        //if (sourcePath.contains("/")) sourcePath = sourcePath.replace("/", File.separator);
        if (sourcePath.endsWith(File.separator)) sourcePath = sourcePath.substring(0, sourcePath.length() - 1);
        if (sourceFile.isDirectory()) {
            fileName = sourcePath.substring(sourcePath.lastIndexOf(File.separator) + 1);
        } else if (sourceFile.isFile()){
            fileName = sourcePath.substring(sourcePath.lastIndexOf(File.separator) + 1, sourcePath.lastIndexOf("."));
            suffix = sourcePath.substring(sourcePath.lastIndexOf("."));
            sourcePath = sourcePath.substring(0, sourcePath.lastIndexOf(fileName));
            filePaths = new ArrayList<>();
            filePaths.add(fileName + suffix);
        }

        File targetFile = null;
        if (targetPath != null && !targetPath.isEmpty()) {
            //if (targetPath.contains("/")) targetPath = targetPath.replace("/", File.separator);
            if (targetPath.endsWith(File.separator)) targetPath = targetPath.substring(0, targetPath.length() - 1);
            targetFile = new File(targetPath);
            if (!targetFile.exists()) targetFile.mkdirs();
            if (targetFile.isFile()) {
                fileName = targetPath.substring(targetPath.lastIndexOf(File.separator) + 1, targetPath.lastIndexOf("."));
                suffix = targetPath.substring(targetPath.lastIndexOf("."));
                targetPath = targetPath.substring(0, targetPath.lastIndexOf(fileName) - 1);
                targetFile = new File(targetPath);
            }
        } else targetPath = sourcePath;

        if (targetFile != null && !targetFile.exists()) targetFile.mkdirs();
        StringBuffer sb = new StringBuffer(RAR_PATH);
        sb.append(String.format(" a %s%s%s.rar", targetPath.replace(" ", "\\ "), File.separator, fileName.replace(" ", "\\ ")));
        if (filePaths != null && filePaths.size() > 0) {
            filePaths.forEach(filePath -> sb.append(String.format(" %s", filePath.replace(" ", "\\ "))));
        } else sb.append(String.format(" %s", sourcePath.substring(sourcePath.lastIndexOf("/") + 1).replace(" ", "\\ ")));
        if (password != null && !password.isEmpty()) sb.append(String.format(" -p%s", password));
        if (volume != null && !volume.isEmpty()) sb.append(String.format(" -v%s", volume));
        if (rr != null) sb.append(String.format(" -rr%s", rr)).append("%");
        String logPath = String.format("%s%s%s.rar.log", targetPath, File.separator, fileName);
        sb.append(" -log=" + logPath.replace(" ", "\\ "));
        System.out.println(sb);
        String finalSourcePath = sourcePath;
        new Thread(() -> {
            try {
                String[] cmd = new String[]{"sh", "-c", sb.toString()};
                Process process = Runtime.getRuntime().exec(cmd, null, new File(finalSourcePath.substring(0, finalSourcePath.lastIndexOf("/"))));
                FileUtil.writeString("", logPath, CharsetUtil.UTF_8);
                InputStreamReader isr = new InputStreamReader(process.getInputStream(), System.getProperty("os.name").contains("Windows") ? "GBK" : "UTF-8");
                //输入命令
                BufferedReader reader = new BufferedReader(isr); // 设置编码为GBK
                int len = 0;
                char[] c = new char[1024];
                String tag = null;
                while ((len = reader.read(c)) != -1) {
                    String line = new String(c, 0, len);
                    //System.err.println(line);
                    int length = line.length();
                    if (StrUtil.containsAny(line, "正在创建", "Creating") && StrUtil.containsAny(line, "正在添加", "Adding", "...")) {
                        //开始时和创建新压缩文件时
                        String[] split = line.split("\r\n\r\n");
                        if (ArrayUtil.length(split) > 1) {
                            logs.add(StrUtil.trimEnd(split[split.length - 2].replace("\r\n", "")).replace("Creating", "正在创建"));
                            if (StrUtil.containsAny(split[split.length - 1], "正在添加", "Adding", "...")) {
                                logs.add(StrUtil.trimEnd(split[split.length - 1].split("     \b\b\b\b")[0].replace("\r\n", "")
                                        .replace("...", "正在添加").replace("\\nAdding data recovery record", "正在添加数据恢复记录")
                                        .replace("\\nAdding", "正在添加")));
                            }
                        }
                    } else if (StrUtil.containsAny(line, "正在添加", "Adding")) {
                        logs.add(StrUtil.trimEnd(line.split("     \b\b\b\b")[0]
                                .replace("Adding data recovery record", "正在添加数据恢复记录")
                                .replace("\r\n", "").replace("\\nAdding", "正在添加")));
                    }
                    String _progress = "";
                    if (StrUtil.containsAny(line, "正在添加", "Adding", "...")) tag = line;
                    if (tag != null && StrUtil.containsAny(tag, "正在添加", "正在创建", "Adding", "Creating")) {
                        if (line.endsWith("%") && length >= 4) {
                            //说明是总进度
                            int p = Integer.parseInt(line.substring(length - 4, length - 1).trim());
                            _progress = p + "%";
                            if (!tag.contains("正在添加数据恢复记录") && !tag.contains("Adding data recovery record")) {
                                progress = p;
                                try {
                                    PreparedStatement ps = DbUtil.connection.prepareStatement("update sn_task set rar_progress = ? where id = ?");
                                    ps.setInt(1, p);
                                    ps.setInt(2, taskId);
                                    ps.executeUpdate();
                                    ps.close();
                                } catch (SQLException e) {
                                    e.printStackTrace();
                                    //throw new RuntimeException(e);
                                }
                            }
                        }
                    }
                    if (line.contains("OK")) _progress = "OK";

                    //System.out.println(progress);
                    _progress = _progress.trim();
                    if ("100%".equals(_progress)) _progress = "OK";
                    if (logs.size() != 0 && StrUtil.isNotBlank(_progress)) {
                        String s = logs.get(logs.size() - 1);
                        if(s.endsWith("%") || s.endsWith("OK")) s = s.substring(0, s.length() - 8);
                        logs.set(logs.size() - 1, s + String.format("%8s", _progress));
                    }
                    if (StrUtil.containsAny(line, "已完成", "Done")) {
                        logs.add(line);
                        try {
                            PreparedStatement ps = DbUtil.connection.prepareStatement("update sn_task set rar_progress = ? where id = ?");
                            ps.setInt(1, 100);
                            ps.setInt(2, taskId);
                            ps.executeUpdate();
                            ps.close();
                        } catch (SQLException e) {
                            e.printStackTrace();
                            //throw new RuntimeException(e);
                        }
                    }
                    //System.err.println(JSONUtil.toJsonStr(logs));
                }
                // 等待命令执行完成
                process.waitFor();
                Thread.sleep(1000 * 60 * 2);
                taskId = null;
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        return logPath;
    }

    public static String compress(String sourcePath, String targetPath, Integer taskId) {
        if (RarUtil.taskId != null) throw new RuntimeException("已有任务在运行中");
        progress = 0;
        RarUtil.taskId = taskId;
        return compress(sourcePath, null, targetPath, ConfigUtil.getConfig().getPassword(), ConfigUtil.getConfig().getVolume(), ConfigUtil.getConfig().getRr());
    }
}
